/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FOLLY_IO_RECORDIO_H_
#error This file may only be included from folly/io/RecordIO.h
#endif

#include <folly/detail/Iterators.h>
#include <folly/hash/SpookyHashV2.h>

namespace folly {

class RecordIOReader::Iterator
    : public detail::IteratorFacade<
          RecordIOReader::Iterator,
          const std::pair<ByteRange, off_t>,
          std::forward_iterator_tag> {
  friend class detail::
      IteratorFacade<Iterator, value_type, std::forward_iterator_tag>;
  friend class RecordIOReader;

 public:
  Iterator() = default;

 private:
  Iterator(ByteRange range, uint32_t fileId, off_t pos);

  reference dereference() const { return recordAndPos_; }
  bool equal(const Iterator& other) const { return range_ == other.range_; }
  void increment() {
    size_t skip = recordio_helpers::headerSize() + recordAndPos_.first.size();
    recordAndPos_.second += off_t(skip);
    range_.advance(skip);
    advanceToValid();
  }

  void advanceToValid();
  ByteRange range_;
  uint32_t fileId_ = 0;
  // stored as a pair so we can return by reference in dereference()
  std::pair<ByteRange, off_t> recordAndPos_;
};

inline auto RecordIOReader::cbegin() const -> Iterator {
  return seek(0);
}
inline auto RecordIOReader::begin() const -> Iterator {
  return cbegin();
}
inline auto RecordIOReader::cend() const -> Iterator {
  return seek(off_t(-1));
}
inline auto RecordIOReader::end() const -> Iterator {
  return cend();
}
inline auto RecordIOReader::seek(off_t pos) const -> Iterator {
  return Iterator(map_.range(), fileId_, pos);
}

namespace recordio_helpers {

namespace recordio_detail {

FOLLY_PACK_PUSH
struct Header {
  // First 4 bytes of SHA1("zuck"), big-endian
  // Any values will do, except that the sequence must not have a
  // repeated prefix (that is, if we see kMagic, we know that the next
  // occurrence must start at least 4 bytes later)
  static constexpr uint32_t kMagic = 0xeac313a1;
  uint32_t magic;
  uint8_t version; // backwards incompatible version, currently 0
  uint8_t hashFunction; // 0 = SpookyHashV2
  uint16_t flags; // reserved (must be 0)
  uint32_t fileId; // unique file ID
  uint32_t dataLength;
  std::size_t dataHash;
  uint32_t headerHash; // must be last
} FOLLY_PACK_ATTR;
FOLLY_PACK_POP

static_assert(
    offsetof(Header, headerHash) + sizeof(Header::headerHash) == sizeof(Header),
    "invalid header layout");

} // namespace recordio_detail

constexpr size_t headerSize() {
  return sizeof(recordio_detail::Header);
}

inline RecordInfo findRecord(ByteRange range, uint32_t fileId) {
  return findRecord(range, range, fileId);
}

} // namespace recordio_helpers

} // namespace folly
